package com.xqkj.commons.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author lwt<br>
 * @description <br>
 * @date 2020/1/6
 */
@Slf4j
public class BeanReflectUtils {

    private static final Map<String, Map<String, Field>> classFieldMaps=new ConcurrentHashMap<>();

    private static final Map<String,Map<String, Method>> classMethodMaps=new ConcurrentHashMap<>();


    private static Map<String, Field> getClassFieldMap(Class clazz){
        Map<String, Field> fieldMap=classFieldMaps.get(clazz.getName());
        if(fieldMap==null){
            synchronized (clazz){
                fieldMap=classFieldMaps.get(clazz.getName());
                if(fieldMap==null){
                    final Map<String, Field> finalFieldMap=new HashMap<>();
                    List<Field> fieldList=getAllFields(clazz);
                    if(CollectionUtils.isEmpty(fieldList)){
                        classFieldMaps.put(clazz.getName(),finalFieldMap);
                        return finalFieldMap;
                    }

                    fieldList.forEach(item->{
                        if(!Modifier.isStatic(item.getModifiers())){
                            finalFieldMap.put(item.getName(),item);
                        }
                    });
                    classFieldMaps.put(clazz.getName(),finalFieldMap);
                    return finalFieldMap;
                }
            }
        }
        return fieldMap;
    }

    private static Map<String, Method> getClassMethodMap(Class clazz){
        Map<String, Method> methodMap=classMethodMaps.get(clazz.getName());
        if(methodMap==null){
            synchronized (clazz){
                methodMap=classMethodMaps.get(clazz.getName());
                if(methodMap==null){
                    final Map<String, Method> finalMethodMap=new HashMap<>();
                    List<Field> fieldList=getAllFields(clazz);
                    if(CollectionUtils.isEmpty(fieldList)){
                        classMethodMaps.put(clazz.getName(),finalMethodMap);
                        return finalMethodMap;
                    }

                    fieldList.forEach(item->{
                        if(!Modifier.isStatic(item.getModifiers())){
                            try{
                                String getMethodName=fiedlNameToGetterName(item.getName());
                                String setMethodName=fiedlNameToSetterName(item.getName());
                                clazz.getMethod(getMethodName);
                                finalMethodMap.put(getMethodName,clazz.getMethod(getMethodName));
                                finalMethodMap.put(setMethodName,clazz.getMethod(setMethodName,item.getType()));

                            }catch (Exception ex){
                            }
                        }
                    });
                    classMethodMaps.put(clazz.getName(),finalMethodMap);
                    return finalMethodMap;
                }
            }
        }
        return methodMap;
    }

    private static List<Field> getAllFields(Class clazz){
        Field[] fields=clazz.getDeclaredFields();
        List<Field> fieldList= new ArrayList<>();
        if(fields!=null && fields.length>0){
            fieldList= Arrays.asList(fields);
        }
        if(clazz.getSuperclass()==null || clazz.getSuperclass().equals(Object.class) ){
            return fieldList;
        }
        List<Field> supperClassFiledList=getAllFields(clazz.getSuperclass());
        if(CollectionUtils.isNotEmpty(supperClassFiledList)){
            fieldList.addAll(supperClassFiledList);
        }
        return fieldList;
    }


    public static Field getModelFieldByName(Class clazz,String fieldName){
        return getClassFieldMap(clazz).get(fieldName);
    }

    public static Method getModelMethodByName(Class clazz,String methodName){
        return getClassMethodMap(clazz).get(methodName);
    }

    public static String fiedlNameToGetterName(String fieldName){
        String getMethodName="get"+XqkjStringUtils.firstCharUp(fieldName);
        return getMethodName;
    }

    public static String fiedlNameToSetterName(String fieldName){
        String getMethodName="set"+XqkjStringUtils.firstCharUp(fieldName);
        return getMethodName;
    }

    public static Object invokeGetMethodByFiledName(String fieldName,Object beanObj){
        if(beanObj==null){
            return null;
        }
        Method method=getModelMethodByName(beanObj.getClass(),fiedlNameToGetterName(fieldName));
        Assert.isNotNull(method,"没有找到"+fieldName+"对应的get方法");
        try {
            Object object = invokeGetMethod(method,beanObj);
            return object;
        } catch (Exception e) {
            log.error("invokeGetMethodByFiledName exception=[{}],fieldName=[{}]",e.getMessage(),fieldName,e);
        }
        return null;
    }

    public static Object invokeGetMethod(Method method,Object beanObj) throws InvocationTargetException, IllegalAccessException {
        return method.invoke(beanObj);
    }
}
